package com.ideaaedi.log4j2.defender.core;

import com.ideaaedi.log4j2.defender.conerter.ConverterConfiguration;
import com.ideaaedi.log4j2.defender.defender.DefaultJsonMessageDefender;
import com.ideaaedi.log4j2.defender.defender.DefaultRegexMessageDefender;
import com.ideaaedi.log4j2.defender.defender.DefaultStringMessageDefender;
import com.ideaaedi.log4j2.defender.defender.Log4j2MessageDefender;
import com.ideaaedi.log4j2.defender.properties.CustomProperties;
import com.ideaaedi.log4j2.defender.properties.DefaultJsonProperties;
import com.ideaaedi.log4j2.defender.properties.DefaultRegexProperties;
import com.ideaaedi.log4j2.defender.properties.DefaultStringProperties;
import com.ideaaedi.log4j2.defender.strategy.RegexDefenderStrategyProvider;
import com.ideaaedi.log4j2.defender.util.DefenderUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.LogEvent;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 脱敏配置类
 *
 * @author JustryDeng
 * @since 2021/7/21 1:15:15
 */
@Configuration
@ConfigurationProperties(prefix = "log.defender")
@ConditionalOnProperty(name = "log.defender.enable", havingValue = "true")
@ImportAutoConfiguration(ConverterConfiguration.class)
public class DefenderConfiguration implements SmartInitializingSingleton {

    /** 是否启用日志脱敏 */
    private boolean enable = false;
    
    /**
     * 调试模式
     * <br/>
     * 注：只有在调试模型下，才会打印脱敏过程中，可能出现的错误。详见{@link Log4j2CoreConverter#format(LogEvent, StringBuilder)} )
     */
    private boolean debug = false;
    
    /** 脱敏方式 */
    private OptEnum opt = OptEnum.DEFAULT_JSON;
    
    /**
     * 要脱敏的loggerName的前缀
     * <p>
     *     loggerName即：创建logger所用类的全类名
     * </p>
     * <p>
     *     此字段和{@link DefenderConfiguration#excludeLoggerPrefix}字段搭配，可灵活控制脱敏范围
     * </p>
     */
    private Set<String> includeLoggerPrefix = new HashSet<>();
    
    /**
     * 不要脱敏的loggerName的前缀
     * <p>
     *     loggerName即：创建logger所用类的全类名
     * </p>
     *     此字段和{@link DefenderConfiguration#includeLoggerPrefix}字段搭配，可灵活控制脱敏范围
     * </p>
     */
    private Set<String> excludeLoggerPrefix = new HashSet<>();
    
    @NestedConfigurationProperty
    public DefaultJsonProperties configJson = new DefaultJsonProperties();
    
    @NestedConfigurationProperty
    public DefaultStringProperties configString = new DefaultStringProperties();
    
    @NestedConfigurationProperty
    public DefaultRegexProperties configRegex = new DefaultRegexProperties();
    
    @NestedConfigurationProperty
    public CustomProperties configCustom = new CustomProperties();
    
    @Override
    public void afterSingletonsInstantiated() {
        // 装配
        Log4j2MessageDefender defender;
        switch (opt) {
            case DEFAULT_JSON:
                defender = new DefaultJsonMessageDefender(DefenderUtil.unfold(configJson.getStrategies()), configJson.getShortCircuitPlugins(), configJson.isHitStringValueIsJson());
                break;
            case DEFAULT_STRING:
                Set<DefaultStringMessageDefender.KeyValueDelimiter> keyValueDelimiter = configString.getKeyValueDelimiter();
                if(CollectionUtils.isEmpty(keyValueDelimiter)) {
                    throw new IllegalArgumentException("Configuration item keyValueDelimiter cannot be empty.");
                }
                defender = new DefaultStringMessageDefender(DefenderUtil.unfold(configString.getStrategies()),
                        configString.getShortCircuitPlugins(),
                        configString.isCompatBackslash(),
                        keyValueDelimiter);
                break;
            case DEFAULT_REGEX:
                RegexDefenderStrategyProvider strategyProvider = configRegex.getStrategyProvider();
                Objects.requireNonNull(strategyProvider, "RegexDefenderStrategyProvider instance cannot be null.");
                defender = new DefaultRegexMessageDefender(strategyProvider.provideRegexStrategyMap(), configRegex.getShortCircuitPlugins());
                break;
            case CUSTOM:
                defender = configCustom.getBean();
                if (defender == null) {
                    throw new IllegalArgumentException("Please specify Log4j2MessageDefender's impl by config [log.defender.config-custom.bean].");
                }
                break;
            default:
                throw new IllegalArgumentException("Cannot recognize log.defender.opt [" + opt + "].");
        }
        // 赋值给log4j2消息转换器
        Log4j2CoreConverter.setDebug(debug);
        Log4j2CoreConverter.setDefender(defender);
        Log4j2CoreConverter.addIncludePackagePrefix(includeLoggerPrefix.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet()));
        Log4j2CoreConverter.addExcludePackagePrefix(excludeLoggerPrefix.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet()));
    }
    
    /**
     * 脱敏方式
     *
     * @author JustryDeng
     * @since 2021/7/23 1:08:36
     */
    public enum OptEnum {
        
        /** json脱敏 */
        DEFAULT_JSON,
        
        /** 字符串脱敏 */
        DEFAULT_STRING,
        
        /** 正则脱敏 */
        DEFAULT_REGEX,
        
        /** 自定义脱敏 */
        CUSTOM
    }
    
    public boolean isDebug() {
        return debug;
    }
    
    public void setDebug(boolean debug) {
        this.debug = debug;
    }
    
    public boolean isEnable() {
        return enable;
    }
    
    public void setEnable(boolean enable) {
        this.enable = enable;
    }
    
    public OptEnum getOpt() {
        return opt;
    }
    
    public void setOpt(OptEnum opt) {
        this.opt = opt;
    }
    
    public Set<String> getIncludeLoggerPrefix() {
        return includeLoggerPrefix;
    }
    
    public void setIncludeLoggerPrefix(Set<String> includeLoggerPrefix) {
        this.includeLoggerPrefix = includeLoggerPrefix;
    }
    
    public Set<String> getExcludeLoggerPrefix() {
        return excludeLoggerPrefix;
    }
    
    public void setExcludeLoggerPrefix(Set<String> excludeLoggerPrefix) {
        this.excludeLoggerPrefix = excludeLoggerPrefix;
    }
    
    public DefaultJsonProperties getConfigJson() {
        return configJson;
    }
    
    public void setConfigJson(DefaultJsonProperties configJson) {
        this.configJson = configJson;
    }
    
    public DefaultStringProperties getConfigString() {
        return configString;
    }
    
    public void setConfigString(DefaultStringProperties configString) {
        this.configString = configString;
    }
    
    public DefaultRegexProperties getConfigRegex() {
        return configRegex;
    }
    
    public void setConfigRegex(DefaultRegexProperties configRegex) {
        this.configRegex = configRegex;
    }
    
    public CustomProperties getConfigCustom() {
        return configCustom;
    }
    
    public void setConfigCustom(CustomProperties configCustom) {
        this.configCustom = configCustom;
    }
}